Docker 内存资源控制
1 背景知识
1、Docker 容器会从宿主机分配尽可能多的内存。这样 Docker 可以进行按需分配。
2、虚拟机内存分配的方式并不灵活,需要实现进行评估。
3、但是 Docker 容器中的进程会分配太多内存,会造成宕机,这是非常不安全的。
4、因此 Docker 提供了一种限制内存消耗的方式,防止内存不足的错误(OOME)发生。
1.1 什么是 OOME
1、当内核检测到没有足够的内存来执行重要的系统功能,它会抛出一个 OOME,即内存不足的异常。
2、如果发生了 OOME,系统将会杀死一些进程保证一定的内存空间。
3、这些进程包括 Docker 容器。
4、更为严重的后果是让整个系统宕机。
1.2 降低 OOME 发生的几率。
可以通过以下方式降低 OOME 发生几率,提高系统稳定性。
1、投入生产之前,需要测试应用的内存需求有多大。
2、评估宿主机是否有足够的内存资源。
3、根据内存需求,限制容器可以使用的最大内存量。
4、根据内存需求,配置 Docker 容器能够使用的最大 Swap 空间。
编者注:在 Docker 主机上配置 Swap 时要注意,Swap 比内存慢,性能也差。
1.3 内存限制的相关参数
这些选项中大多数都是一个正整数,(b、k、m、g)表示字节、KB、MB、GB。
选项 | 描述 |
---|---|
-m,–-memory | 容器可以使用的最大内存量,格式是数字加单位,单位可以为 b, k, m, g。最小为 6 M |
--memory-swap | 容器可以使用交换空间的总大小。格式同上。详细说明请参见--memory-swap 参数详解。 |
--memory-reservation | 容器内存的软性限制。当 Docker 检测到主机上额争用或低内存时,此参数将会被激活。当配置这个参数时,它必须要比 --memory 要低。由于是软性限制,它可以超过容器的内存限制。 |
--oom-kill-disable | 默认情况下,如果发生内存不足(OOM)错误,容器将会杀死容器中的进程,如果要禁用功能请设置此选项,请先配置 -m 选项,才会禁用此参数,当 -m 未设置时,内核只会杀死宿主机上的相关进程。 |
--oom-score-adj | 容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0 |
--memory-swappiness | 默认情况下,容器可以使用交换空间一定比例。值为 0~100 之间的整数 |
--kernel-memory | 容器可以使用的最大内核内存限制。格式同上,最小为 4 M ,内核内存不允许存放在交换空间,此参数设置不当,可能会对宿主机和其他容器造成影响。 |
1.4 --memory-swap 参数详解
1、这个参数是一个开关变量参数,只有 --memory
参数设置时才有意义。
2、开启这个参数之后将会把磁盘上某一部分空间当做内存使用。
3、虽然降低了 OOME 错误的发生几率,但会有性能上的损失。
4、以下为设置的参数的 5 种场景。
--memory-swap | --memory | 说明 |
---|---|---|
正整数 | 正整数 | 参数设置为 --memory="300m" 和 --memory-swap="1g" 这个容器将会有 300 M 内存使用空间和 700 M (1 g-300 m) 交换空间。 |
0 | N/A | 容器会忽略这个参数。 |
300 M | 300 M | 当参数 --memory-swap 和 --memory 相同,且 --memory 是个正整数,容器将不会使用交换空间。 |
未设置 | 正整数 | 容器可以使用 --memory 相同大小的交换空间。例如:在一个开启交换空间的 Linux 系统中,--memory="300m" 并且 --memory-swap 未设置,则容器可以使用 600M 内存空间。 |
-1 | N/A | 则允许容器无限制的使用交换量,最大的大小由宿主机决定。 |
编者注:在容器内部执行 free
工具现实的是主机的可用交换空间,而不是容器内部的可用空间。
1.5 --memory-swappiness 参数详解
1、当参数设置为 0,将禁止使用所有交换空间页面。
2、当参数设置为 100 时,将允许使用所有交换空间页面。
3、当参数未设置时,将会从宿主机中继承使用策略。
1.6 --kernel-memory 参数详解(这个有问题,没搞懂)
--kernel-memory
是指已获得的总内存,称之为 内核内存
。
--memory
是指限制 Docker 容器详情请看以下场景。
1、容器可获取无限的内存,无限的内核内存(总内存)。这个是容器的默认行为。
2、容器获取无限的内存,有限的内核内存(总内存)。
可以通过 Cgroups 资源配置内核内存,使其永远不能超过主机的上的可用内存。
3、有限的内存,无限的内核内存。
主机内存是有限的的,但是内核内存不是。
3、有限的内存,有限的内核内存。
(1)如果限制了容器两种类型的内存,它会耗尽内存,而不影响其他的容器和主机。
(2)如果内核内存限制低于用户内存限制,内核内存会耗尽,导致容器出现 OOM 错误。
(3)如果内核内存高于用户内存限制,那么内核限制就不容易导致 OOM。
2 配置 Swap 空间
docker info
如果输出有一行 No swap limit support
,那么需要你自己开启宿主机交换空间功能。
获取更多信息: https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-centos-7
3 限制容器内存
3.1 运行一个限制内存为 6 M 的容器
docker run -it -m 6m centos:7 bash
3.2 启动一个 10 MB 的应用程序
1、这里使用 python 打开一个设备文件。
2、从 /dev/zero
设备读取 10 M 数据到内存中。
3、消耗太多内存,此应用程序被杀死。
[root@de695e96f0ee /]# python -c 'open("/dev/zero").read(10*1024*1024)'
Killed
3.3 启动一个 10 MB 的 SHELL 程序
1、使用 dd 命令从 /dev/zero
设备文件中拷贝 10 M 内容并进行 base 64 加密。
2、bash 被杀死后,容器退出。
3、检查退出码为 137。
4、退货码非零,表示容器因错退出。
[ root@de695e96f0ee /]# \
> A=$(dd if=/dev/zero bs=1 M count=10 | base64)
[ root@node1 ~]# echo $?
137
[ root@node1 ~]#
3.4 使用 stress
镜像进行内存测试。
使用 jess/stress 镜像,这一镜像里面包含有 stress,一个用来测试系统限制的工具。
1、运行一个占用内存空间为 150MB
的压力测试环境。
$ docker run -m 100m jess/stress --vm 1 --vm-bytes 150m --vm-hang 0
编者注:当-m
选项设置为100m,且--memory-swap
未设置时,Swap 空间 + 内存空间=200M,所以压力测试进程能够正常运行。
2、命令说明。
选项 | 说明 |
---|---|
-m 100m |
容器内存限制为 100 MB。 |
--vm-bytes |
stress 工具需要 150 MB。 |
--vm-hang 0 |
指示每个消耗内存的进程在分配到内存后转入睡眠状态 N 秒,然后释放内存,一直重复执行这个过程。 |
--vm 1 |
产生 n 个进程, 每个进程不断调用内存分配 malloc 和内存释放 free 函数。 |
3、查看容器占用的内存空间
$ docker top a70fa7b620f9 -eo pid,size,args
4、运行一个占用内存空间为 250MB
的压力测试环境。
$ docker run -m 100m jess/stress --vm 1 --vm-bytes 250m --vm-hang 0
编者注:当 -m
选项设置为100m,且 --memory-swap
未设置时,Swap 空间 + 内存空间=200M,所以压力测试进程会被立刻杀死。